#undef  xuser
#define xuser mixc::concurrency_process::inc
#include"concurrency/process.hpp"
#include"macro/xcstyle.hpp"
#include"macro/xinit.hpp"
#include"macro/xthread_local.hpp"

#if xuse_mixc_rtos
    #pragma warning "not support"
#elif xis_windows
    #include<windows.h>
#elif xis_linux
    #include<malloc.h>
    #include<signal.h>
    #include<sys/wait.h>
    #include<unistd.h>
#endif

namespace mixc::concurrency_process::inc{
#if xis_linux
    enum class pipe_state_t : u08 {
        ok,
        broken,
    };

    xthread_local(l_is_pipe_broken, volatile pipe_state_t) = xthread_local_new(pipe_state_t::ok);

    xinit(inc::the_concurrency_process){
        signal(SIGPIPE, xcstyle(int){
            *l_is_pipe_broken = pipe_state_t::broken;
        });
    };

    inline uxx read(ixx fd, voidp buffer, uxx bytes, pipe_state_t * state = nullptr){
        ssize_t real_bytes;
        *l_is_pipe_broken   = pipe_state_t::ok;
        real_bytes          = ::read(fd, buffer, bytes);
        
        if (state){
            state[0]        = *l_is_pipe_broken;
        }
        return uxx(real_bytes);
    }

    inline uxx write(ixx fd, const void * buffer, uxx bytes, pipe_state_t * state = nullptr){
        ssize_t real_write;
        *l_is_pipe_broken   = pipe_state_t::ok;
        real_write          = ::write(fd, buffer, bytes);

        if (state){
            state[0]        = *l_is_pipe_broken;
        }
        return uxx(real_write);
    }

#endif

    inline void close_with_clear(auto * fd){
    #if xis_windows
    #elif xis_linux
        close(*fd);
        *fd = 0;
    #endif
    }
}

namespace mixc::concurrency_process::origin{
    process::~process(){
    #if xis_windows
        #pragma error
    #elif xis_linux
        if (m_pid > 0){
            waitpid(m_pid, NULL, 0);
            m_pid = 0;
        }
        if (m_tx > 0){
            close(m_tx);
            m_tx = 0;
        }
        if (m_rx > 0){
            close(m_rx);
            m_rx = 0;
        }
    #endif
    }

    uxx process::read(voidp buffer, uxx bytes) const {
        if (m_rx == 0){
            return not_exist;
        }

    #if xis_windows
        #pragma error
    #elif xis_linux
        return inc::read(m_rx, buffer, bytes);
    #endif
    }

    uxx process::write(const void * buffer, uxx bytes) const {
        if (m_tx == 0){
            return not_exist;
        }

    #if xis_windows
        #pragma error
    #elif xis_linux
        return inc::write(m_tx, buffer, bytes);
    #endif
    }

    i32 process::wait_exit() const {
        i32 exit_code   = 0;

    #if xis_windows
        #pragma error
    #elif xis_linux
        waitpid(m_pid, xmodify(exit_code), 0);
        m_pid           = 0;
        return exit_code;
    #endif
    }

    bstate_t process::open_core(inc::c08 const & cmd_path, process_mode_t mode, const inc::c08 cmd_list[], uxx cmd_length) const {
    #if xis_windows
        #pragma error
    #elif xis_linux
        enum{
            parent_read,
            child_write,
            child_read,
            parent_write,
            max_number,
        };

        if (m_pid){
            return bstate_t::fail;
        }

        i32 pipe_fd[max_number] = {};
        auto success    = false;
        auto i_split    = cmd_path.index_of_last('/'); // cmd 格式 /path/to/exec

        if (i_split == not_exist and i_split + 1 < cmd_path->length){
            return bstate_t::fail;
        }

        xdefer{
            if (success){
                return;
            }

            for(auto fd : pipe_fd){
                if (fd){
                    close(fd);
                }
            }
        };

        if (mode != process_mode_t::pipe_none){
            if (pipe(pipe_fd) < 0){
                return bstate_t::fail;
            }

            if (pipe(pipe_fd + 2) < 0){
                return bstate_t::fail;
            }

            switch(mode){
            case process_mode_t::pipe_write_only:
                m_tx = pipe_fd[parent_write];
                break;
            case process_mode_t::pipe_read_only:
                m_rx = pipe_fd[parent_read];
                break;
            case process_mode_t::pipe_read_write:
                m_tx = pipe_fd[parent_write];
                m_rx = pipe_fd[parent_read];
                break;
            default:
                break;
            }
        }

        // 创建失败
        if (m_pid = fork(); m_pid < 0){
            return bstate_t::fail;
        }

        // 父进程
        if (m_pid != 0){
            switch(mode){
            case process_mode_t::pipe_write_only: inc::close_with_clear(xmodify(pipe_fd[parent_read])); break;
            case process_mode_t::pipe_read_only: inc::close_with_clear(xmodify(pipe_fd[parent_write])); break;
            default:
                break;
            }

            // 父进程不会用到子进程的管道句柄，直接关闭（在 fork 时子进程也会克隆一份 child_read、child_write 句柄）
            // 这里关闭句柄只是让句柄计数器 - 1，避免句柄泄漏
            success = true;
            inc::close_with_clear(xmodify(pipe_fd[child_read]));
            inc::close_with_clear(xmodify(pipe_fd[child_write]));
            return bstate_t::success;
        }
        // 子进程
        else{
            switch(mode){
            case process_mode_t::pipe_none:
                break;
            case process_mode_t::pipe_write_only:
                dup2(pipe_fd[child_read], STDIN_FILENO);
                inc::close_with_clear(xmodify(pipe_fd[child_write]));
                break;
            case process_mode_t::pipe_read_only:
                dup2(pipe_fd[child_write], STDOUT_FILENO);
                inc::close_with_clear(xmodify(pipe_fd[child_read]));
                break;
            case process_mode_t::pipe_read_write:
                dup2(pipe_fd[child_read], STDIN_FILENO);
                dup2(pipe_fd[child_write], STDOUT_FILENO);
                break;
            default:
                break;
            }

            inc::close_with_clear(xmodify(pipe_fd[parent_read]));
            inc::close_with_clear(xmodify(pipe_fd[parent_write]));
        }

        auto cmd_name   = cmd_path.backward(i_split + 1);
        auto convert    = [](inc::c08 const & str) -> char * {
            // 以 \0 结束，直接返回 str
            if (str->is_cstr_compatible){
                return (char *)(str);
            }
            // 否则需要在末尾追加 \0
            else{
                return (char *)(str.clone([](uxx length){
                    auto buffer = (char *)malloc(length + 1); // 无需考虑释放问题，子进程退出后会释放内存
                    buffer[length] = '\0';
                    return buffer;
                }));
            }
        };

        auto path       = convert(cmd_path);
        auto i_end      = cmd_length + 1;
        auto list       = (char **)malloc((1/*cmd_name*/ + cmd_length + 1/*nullptr*/) * sizeof(char *));
        list[0]         = convert(cmd_name);
        list[i_end]     = nullptr;

        for(uxx i = 0; i < cmd_length; i++){
            list[i + 1] = convert(cmd_list[i]);
        }

        execv(path, list);
        _exit(-1);

        // execv() 执行成功将退出进程，如果失败将执行到 _exit() 直接退出进程
        return bstate_t::success;
    #endif
    }
}
